package com.czkevin.guicontrolsapi;

import com.comphenix.protocol.ProtocolLibrary;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.*;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.java.JavaPlugin;
import com.czkevin.guicontrolsapi.controls.GUIControl;
import com.czkevin.guicontrolsapi.tests.GUITestCommand;

public final class GUIControlsAPI extends JavaPlugin implements Listener {

    private static GUIControlsAPI instance;

    public static GUIControlsAPI getInstance() {
        return instance;
    }

    private InputWindowManager inputWindowManager;

    public InputWindowManager getInputWindowManager() {
        return inputWindowManager;
    }

    @Override
    public void onLoad() {
        instance = this;
        inputWindowManager = new InputWindowManager(this);
        ProtocolLibrary.getProtocolManager().addPacketListener(inputWindowManager);
    }

    @Override
    public void onEnable() {
        // Plugin startup logic
        getServer().getPluginManager().registerEvents(this, this);
        getServer().getPluginManager().registerEvents(inputWindowManager, this);
        inputWindowManager.onReload();

        getCommand("guitest").setExecutor(new GUITestCommand());
        getCommand("gui-anvil-id").setExecutor((sender, command, label, args) -> {
            if(!sender.hasPermission("gui.set.anvil-id")) return true;
            final int x = Integer.parseInt(args[0]);
            InputWindowManager.ANVIL_WINDOW_ID = x;
            sender.sendMessage("> " + x);
            return true;
        });
    }

    @EventHandler(priority = EventPriority.HIGHEST)
    public void onInventoryOpen(InventoryOpenEvent e) {
        if(Player.class.isAssignableFrom(e.getPlayer().getClass()) && inputWindowManager.isOpened((Player) e.getPlayer())) {
            e.setCancelled(true);
            return;
        }
        Window w = Window.get(e);
        if(w == null) return;
        w.onOpen(e.getPlayer());
    }

    @EventHandler(priority = EventPriority.HIGHEST)
    public void onWindowClose(InventoryCloseEvent e) {
        Inventory inv = e.getInventory();
        Window w = Window.get(inv);
        if(w == null) {
            if(inv.getHolder() != null && WithParentWindow.class.isAssignableFrom(inv.getHolder().getClass())) {
                Window parent = ((WithParentWindow) inv.getHolder()).getParent(e.getPlayer());
                if(parent != null) {
                    parent.open(e.getPlayer(), null);
                }
            }
            return;
        }
        boolean closed = w.onClose(e.getPlayer());
        if(!closed) {
            Bukkit.getScheduler().runTaskLater(this, () -> e.getPlayer().openInventory(w.getInventory()), 1L);
        } else {
            w.close(e.getPlayer());
        }
    }

    @EventHandler(priority = EventPriority.HIGHEST)
    public void onWindowDrag(InventoryDragEvent event) {
        Window w = Window.get(event.getView().getTopInventory());
        if(w != null) event.setCancelled(true);
    }

    @EventHandler(priority = EventPriority.HIGHEST)
    public void onWindowManipulate(InventoryClickEvent event) {
        if(event.getClickedInventory() == null) return;
        if(event.getClickedInventory() != event.getView().getTopInventory()) {
            InventoryHolder topHolder = event.getView().getTopInventory().getHolder();
            if(topHolder == null || !Window.class.isAssignableFrom(topHolder.getClass())) return;
            // Lower inventory
            if(!event.getAction().name().startsWith("PICKUP_") && !event.getAction().name().startsWith("PLACE_")) {
                event.setCancelled(true);
                return;
            }
        }
        Window w = Window.get(event.getClickedInventory());
        if(Window.get(event.getView().getTopInventory()) != null) {
            if(event.isShiftClick() || event.getClick() == ClickType.CONTROL_DROP) {
                event.setCancelled(true);
                return;
            }
        }
        if(w == null) return;
        event.setCancelled(true); // default to cancelled
        if(event.getClickedInventory() == null) {
            w.onClickedOutside(event);
            return;
        }
        int ox = w.getOffsetX();
        int oy = w.getOffsetY();
        int slot = event.getSlot();
        int sx = slot % 9;
        int sy = slot / 9;
        if(sx == 0 && sy == w.getScreenHeight()) { // left
            event.setCancelled(true);
            w.setOffset(ox-1, oy);
            return;
        }
        if(sx == 7 && sy == w.getScreenHeight()) { // right
            event.setCancelled(true);
            w.setOffset(ox+1, oy);
            return;
        }
        if(sx == 8 && sy == w.getScreenHeight()-1) { // down
            event.setCancelled(true);
            w.setOffset(ox, oy+1);
            return;
        }
        if(sx == 8 && sy == 0) { // up
            event.setCancelled(true);
            w.setOffset(ox, oy-1);
            return;
        }

        int dx = ox + sx;
        int dy = oy + sy;
        // System.out.println(String.format("clicked display (%d, %d)", dx, dy));
        GUIControl c = w.getControlAt(dx, dy);
        if(c != null) {
            try {
                c.onClick(event, dx - c.getX(), dy - c.getY());
            } catch (Exception e) {
                e.printStackTrace();
                event.setCancelled(true);
            }
        }
    }

    @EventHandler
    public void onPlayerDisconnect(PlayerQuitEvent event) {
        InventoryView view = event.getPlayer().getOpenInventory();
        if(view.getTopInventory().getHolder() != null && Window.class.isAssignableFrom(view.getTopInventory().getHolder().getClass())) {
            System.out.println("close because disconnection");
            Window w = (Window) view.getTopInventory().getHolder();
            w.close(event.getPlayer());
        }
    }
}
